
counter = 0

err_details = "err_details"

err_handler = """
#define ERRCHECK(code) { \\
  arbb_error_t err = (code); \\
  if (err != arbb_error_none) { \\
    fprintf(stderr, "ARBB ERR: %s(code %d) %s:%d\\n", arbb_get_error_message(err_details), arbb_get_error_code(err_details), __FILE__, __LINE__); \\
  } \\
}
"""

def genUniqueID():
    global counter
    counter = counter + 1
    return counter

class Context:
    def __init__(self):
        self.name = "ctx" + str(genUniqueID())

    def emit(self):
        s = "// Context\n"
        s += "arbb_context_t %s;\n" % (self.name);
        s += "ERRCHECK(arbb_get_default_context(&%s, &%s));\n" % (self.name, err_details)

        return s

    def __str__(self):
        return self.name

class ScalarTy:
    def __init__(self, ctx, ty):
        self.name = "ty" + str(genUniqueID())
        self.ty = ty
        self.ctx = ctx
        
    def emit(self):

        s = "arbb_type_t %s;\n" % self.name
        s += "ERRCHECK(arbb_get_scalar_type(%s, &%s, arbb_%s, &%s));\n" % (self.ctx, self.name, self.ty, err_details)

        return s

    def __str__(self):
        return self.name

class AssignOp:
    def __init__(self, fun, dst, src):
        self.name   = "assign" + str(genUniqueID())
        self.fun    = fun
        self.dst    = dst
        self.src    = src

    def emit(self):

        # supports one output, 2 inputs
        s = ""
        s += "ERRCHECK(arbb_op(%s, arbb_op_copy, &%s, &%s, NULL, &%s));\n" % (self.fun, self.dst, self.src, err_details)

        return s

class BinOp:
    def __init__(self, fun, op, dst, src0, src1):
        self.name   = "binop" + str(genUniqueID())
        self.fun    = fun
        self.op     = op
        self.dst    = dst
        self.src0   = src0
        self.src1   = src1

    def emit(self):

        # supports one output, 2 inputs
        s = ""
        s += "arbb_variable_t out_%s = %s;\n" % (self.name, self.dst)
        s += "arbb_variable_t in_%s[] = {%s, %s};\n" % (self.name, self.src0, self.src1)
        s += "ERRCHECK(arbb_op(%s, arbb_op_%s, &out_%s, in_%s, NULL, &%s));\n" % (self.fun, self.op, self.name, self.name, err_details)

        return s

# assume float value only.
class Const:
    def __init__(self, ctx, val):
        self.ctx  = ctx
        self.name = "const" + str(genUniqueID())
        self.ty   = ScalarTy(ctx, 'f32')
        self.val  = val

    def emit(self):
        tmpname = "tmp_" + self.name
        s = "float %s = %f;\n" % (tmpname, self.val)

        s += "ERRCHECK(arbb_create_constant(%s, &%s, %s, &%s, NULL, &%s))" % (self.ctx, self.name, self.ty, tmpname, err_details)

        return s


class GetParam:
    def __init__(self, fun, varname, isout, idx):
        self.fun     = fun
        self.varname = varname
        self.isout   = isout
        self.idx     = idx

    def emit(self):
        s = "arbb_variable_t %s;\n" % self.varname
        s += "ERRCHECK(arbb_get_parameter(%s, &%s, %d, %d, &%s));\n" % (self.fun, self.varname, self.isout, self.idx, err_details)
        return s


class Function:
    def __init__(self, ctx, name, outTys, inTys):
        self.ctx        = ctx
        self.fun        = "fun" + str(genUniqueID())
        self.name       = name
        self.outTys     = outTys
        self.inTys      = inTys
        self.insts      = []

        self.funTy      = "funTy" + str(genUniqueID())

    def emitGetFunctionType(self, inTys, outTys):
        s = "ERRCHECK(arbb_get_function_type(%s, &%s, %d, %s, %d, %s, &%s));\n" % (self.ctx, self.funTy, len(outTys), len(inTys), err_details)

    def emit(self):
        s = "// Function: %s\n" % self.name

        # fun ty def
        for ty in self.outTys:
            s += "%s" % ty.emit()

        s += "arbb_type_t out%s[] = {" % self.funTy
        for (n, ty) in enumerate(self.outTys):
            s += "%s, " % ty
        s += "};\n"

        for ty in self.inTys:
            s += "%s" % ty.emit()

        s += "arbb_type_t in%s[] = {" % self.funTy
        for (n, ty) in enumerate(self.inTys):
            s += "%s, " % ty
        s += "};\n"

        # fun ty
        s += "\n"
        s += "arbb_type_t %s;\n" % self.funTy
        s += "ERRCHECK(arbb_get_function_type(%s, &%s, %d, out%s, %d, in%s, &%s));\n" % (self.ctx, self.funTy, len(self.outTys), self.funTy, len(self.inTys), self.funTy, err_details)

        # fun body
        s += "arbb_function_t %s;\n" % self.fun
        s += "ERRCHECK(arbb_begin_function(%s, &%s, %s, \"%s\", 0, &%s));\n" % (self.ctx, self.fun,self.funTy, self.name, err_details)

        # todo..
        for inst in self.insts:
            print inst
            s += inst.emit()

        s += "ERRCHECK(arbb_end_function(%s, &%s));\n" % (self.fun, err_details)

        s += "ERRCHECK(arbb_compile(%s, &%s));\n" % (self.fun, err_details)
        s += "funtest(%s, %s);\n" % (self.ctx, self.fun)

        return s

    def __str__(self):
        return self.fun


class If:
    def __init__(self, fun, cond):
        self.fun  = fun
        self.cond = cond

    def emit(self):
        s = "ERRCHECK(arbb_if(%s, %s, &%s));\n" % (self.fun, self.cond, err_details)
        return s

class Else:
    def __init__(self, fun):
        self.fun  = fun

    def emit(self):
        s = "ERRCHECK(arbb_else(%s, &%s));\n" % (self.fun, err_details)
        return s

class EndIf:
    def __init__(self, fun):
        self.fun  = fun

    def emit(self):
        s = "ERRCHECK(arbb_end_if(%s, &%s));\n" % (self.fun, err_details)
        return s


class Variable:
    def __init(self, name):
        self.name = name

class LocalVariable:
    def __init__(self, fun, ty, name):
        self.fun = fun
        self.ty  = ty
        self.name = name
        self.var  = "var" + name 

    def emit(self):

        s = "%s" % self.ty.emit()
        s += "arbb_variable_t %s;\n" % (self.var)
        s += "ERRCHECK(arbb_create_local(%s, &%s, %s, \"%s\", &%s));\n" % (self.fun, self.var, self.ty, self.name, err_details)

        return s

class Call:
    def __init__(self, caller, callee, outs, ins):
        self.caller = caller
        self.callee = callee
        self.outs   = outs
        self.ins    = ins

    def emit(self):

        s = "ERRCHECK(arbb_call_op(%s, arbb_op_call, %s, &%s, &%s, &%s));\n" % (self.caller, self.calle, self.outs, self.ins, err_details)

        return s

class GlobalVariable:
    def __init(self, name):
        self.name = name

def emitHeader():
    s = "#include <arbb_vmapi.h>\n"
    s += "#include <stdio.h>\n"
    s += "#include <stdlib.h>\n"
    s += "\n"
    s += "#include \"test.h\"\n"
    s += err_handler
    s += "int main(int argc, char **argv) {\n"

    s += "arbb_error_details_t %s;\n" % (err_details)

    return s

def emitFooter():
    s = "  printf(\"ArBB exec done.\\n\");\n"
    s += "  return 0;\n"
    s += "}\n"

    return s

def emitCode(ctx, fns):

    f = open("out.c", "w")

    print >>f, emitHeader()

    print >>f, ctx.emit()

    for fn in fns:
        print >>f, fn.emit()

    print >>f, emitFooter()

def main():
    ctx = Context()
    ty = ScalarTy(ctx, 'f32')
    print ty

    outTys = [ScalarTy(ctx, 'f32')]
    inTys = [ScalarTy(ctx, 'f32'), ScalarTy(ctx, 'f32')]

    fconst = Const(ctx, 1.42)
    print fconst.emit()

    print ctx

    fun = Function(ctx, "myfunc", outTys, inTys)
    
    f = open("out.c", "w")

    print >>f, emitHeader()
    print >>f, ctx.emit()
    print >>f, fun.emit()
    print >>f, emitFooter()

    f.close()

if __name__ == '__main__':
    main()
